/*
 * ContextToJenaTranslator.java
 *
 * Created on 29 de junio de 2006, 12:59
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package ocp.management;

import com.hp.hpl.jena.ontology.*;
import com.hp.hpl.jena.rdf.model.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
import java.util.Hashtable;
import ocp.context.*;

/**
 * This class handles the translation between contexts and the statements of
 * the Jena database.
 * 
 */
public class ContextToJenaTranslator {
    protected OntModel model;
    protected static final String MODELNAME = "file://eu/popeye/resources/context.owl#";

    /**
     * Creates a new instance of ContextToJenaTranslator
     */
    public ContextToJenaTranslator(OntModel model) {
        this.model = model;
    }

    public void addContext(Context c) throws ContextException {

        if (c instanceof SimpleContext) {
            //model.begin();
            try {
                this.addSimpleContext((SimpleContext) c);
            } catch (ContextException ex) {
                ex.printStackTrace();
            //  model.abort();
            }
        // model.commit();
        } else if (c instanceof ComplexContext) {
            //  model.begin();
            try {
                this.addComplexContext((ComplexContext) c);
            } catch (ContextException ex) {
                ex.printStackTrace();
            //    model.abort();
            }
        //model.commit();
        } else {
            throw new ContextException("Unknown context. SimpleContext or ComplexContext expected.");
        }
    }

    /**
     * Adds a SimpleContext to the Jena Database.
     */
    public Resource addSimpleContext(SimpleContext sc) throws ContextException {

        OntClass simpleContext = model.getOntClass(MODELNAME + "SimpleContext");
        Individual simpleInd = model.getIndividual(sc.getId());

        simpleInd = null;
        if (simpleInd == null) {
            simpleInd = model.createIndividual(sc.getId(), simpleContext);

            // datatype properties
            DatatypeProperty idProp = model.getDatatypeProperty(MODELNAME + "id");
            simpleInd.addProperty(idProp, sc.getId());
            DatatypeProperty nameProp = model.getDatatypeProperty(MODELNAME + "name");
            simpleInd.addProperty(nameProp, sc.getName());

            DatatypeProperty timeProp = model.getDatatypeProperty(MODELNAME + "time");
            simpleInd.addProperty(timeProp, sc.getTime());

            // simple context object properties
            ObjectProperty notInf = model.getObjectProperty(MODELNAME + "notifierInformation");
            Individual notInfInd = (Individual) addComponentAddress(sc.getNotifierInformation());
            if (notInfInd != null) {
                simpleInd.addProperty(notInf, notInfInd);
            }
            ObjectProperty souInf = model.getObjectProperty(MODELNAME + "sourceInformation");
            Individual souInfInd = (Individual) addComponentAddress(sc.getSourceInformation());
            if (souInfInd != null) {
                simpleInd.addProperty(souInf, souInfInd);
            }
            try {
                ObjectProperty data = model.getObjectProperty(MODELNAME + "data");
                for (Enumeration e = sc.getContextualInformation().getContextualInformation(); e.hasMoreElements();) {
                    ContextualInformation ci = (ContextualInformation) e.nextElement();
                    if (ci.getContextClass() != null) {
                        if (ci.getContextClass().getHistorical()) {
                            addContextToHistorical(sc);
                        }
                    }
                    Individual ciInd = (Individual) addContextualInformation(ci);
                    simpleInd.addProperty(data, ciInd);
                }
            } catch (ContextualInformationAbsentException ex) {
                ex.printStackTrace();
                //throw new ContextException("Unable to get contextual information from simple context "+sc.getId());
                System.err.println("Unable to get contextual information from simple context " + sc.getId());
            }
        }
        return simpleInd;
    }

    /**
     * Adds a ComplexContext to the Jena Database
     */
    public Resource addComplexContext(ComplexContext cc) throws ContextException {
        OntClass complexContext = model.getOntClass(MODELNAME + "ComplexContext");
        Individual complexInd = model.getIndividual(cc.getId());
        complexInd = null;
        if (complexInd == null) {
            complexInd = model.createIndividual(cc.getId(), complexContext);
            // complex context datatype properties.
            DatatypeProperty idProp = model.getDatatypeProperty(MODELNAME + "id");
            complexInd.addProperty(idProp, cc.getId());
            DatatypeProperty nameProp = model.getDatatypeProperty(MODELNAME + "name");
            complexInd.addProperty(nameProp, cc.getName());

            DatatypeProperty timeProp = model.getDatatypeProperty(MODELNAME + "time");
            complexInd.addProperty(timeProp, cc.getTime());

            // complex context object properties.
            ObjectProperty notInf = model.getObjectProperty(MODELNAME + "notifierInformation");
            Individual notInfInd = (Individual) addComponentAddress(cc.getNotifierInformation());
            if (notInfInd != null) {
                complexInd.addProperty(notInf, notInfInd);
            }
            ObjectProperty souInf = model.getObjectProperty(MODELNAME + "sourceInformation");
            Individual souInfInd = (Individual) addComponentAddress(cc.getSourceInformation());
            if (souInfInd != null) {
                complexInd.addProperty(souInf, souInfInd);
            }
            ObjectProperty contexts = model.getObjectProperty(MODELNAME + "contexts");
            Individual cc2;
            for (Iterator i = cc.getContexts(); i.hasNext();) {
                Context c = (Context) i.next();
                if (c instanceof ComplexContext) {
                    cc2 = (Individual) addComplexContext((ComplexContext) c);
                } else if (c instanceof SimpleContext) {
                    cc2 = (Individual) addSimpleContext((SimpleContext) c);
                } else {
                    throw new ContextException("Unknown context. SimpleContext or ComplexContext expected.");
                }
                complexInd.addProperty(contexts, cc2);
            }
        }
        // we return the complex context.
        return complexInd;
    }

    /**
     * adds a component address to the jena database
     */
    public Resource addComponentAddress(ComponentAddress ca) throws ContextException {
        if (ca != null) {
            OntClass compClass = model.getOntClass(MODELNAME + "ComponentAddress");
            Individual indComp = model.getIndividual(ca.getId());
            if (indComp == null) {
                indComp = model.createIndividual(ca.getId(), compClass);

                DatatypeProperty idProp = model.getDatatypeProperty(MODELNAME + "id");
                indComp.addProperty(idProp, ca.getId());

                DatatypeProperty addressProp = model.getDatatypeProperty(MODELNAME + "address");
                indComp.addProperty(addressProp, ca.getAddress());
            }

            return indComp;
        }
        return null;
    }

    /**
     * adds a contextual information to the jena database
     */
    public Resource addContextualInformation(ContextualInformation ci) throws ContextException {
        OntClass ciClass = model.getOntClass(MODELNAME + "ContextualInformation");
        Individual ciInd = model.getIndividual(ci.getName());

        ciInd = null;

        if (ciInd == null) {
            ciInd = model.createIndividual(ci.getName(), ciClass);

            // name, description
            DatatypeProperty nameProp = model.getDatatypeProperty(MODELNAME + "name");
            ciInd.addProperty(nameProp, ci.getName());
            DatatypeProperty descProp = model.getDatatypeProperty(MODELNAME + "description");
            ciInd.addProperty(descProp, ci.getDescription());

            // attributes
            ObjectProperty attProp = model.getObjectProperty(MODELNAME + "attributes");
            for (Enumeration e = ci.getAttributes(); e.hasMoreElements();) {
                Object sig = e.nextElement();
                Attribute a = (Attribute) sig;
                Individual attInd = (Individual) addAttribute(a, ci.getName());
                ciInd.addProperty(attProp, attInd);
            }

            // class
            ObjectProperty classProp = model.getObjectProperty(MODELNAME + "class");
            Individual classInd = (Individual) addClass(ci.getContextClass());
            ciInd.addProperty(classProp, classInd);

            // different instances
            ObjectProperty diProp = model.getObjectProperty(MODELNAME + "differentInstances");
            for (Enumeration e = ci.getDifferentInstances().getContextualInformation(); e.hasMoreElements();) {
                Individual diInd = (Individual) addContextualInformation((ContextualInformation) e.nextElement());
                ciInd.addProperty(diProp, diInd);
            }

            // equivalent instances
            ObjectProperty eiProp = model.getObjectProperty(MODELNAME + "equivalentInstances");
            for (Enumeration e = ci.getEquivalentInstances().getContextualInformation(); e.hasMoreElements();) {
                Individual eiInd = (Individual) addContextualInformation((ContextualInformation) e.nextElement());
                ciInd.addProperty(eiProp, eiInd);
            }

            // properties
            ObjectProperty propProp = model.getObjectProperty(MODELNAME + "properties");

            for (Enumeration e = ci.getProperties(); e.hasMoreElements();) {
                Individual propInd = (Individual) addProperty((ContextProperty) e.nextElement(), ci.getName());
                ciInd.addProperty(propProp, propInd);
            }
        }
        return ciInd;
    }

    /**
     * adds an attribute to the jena database
     */
    public Resource addAttribute(Attribute att, String ciId) throws ContextException {
        OntClass attClass = model.getOntClass(MODELNAME + "Attribute");
        Individual attInd = model.getIndividual(ciId + "-" + att.getAttributeDefinition().getName() + ":" + att.getAttributeDefinition().getTypeName());
        if (attInd == null) {
            attInd = model.createIndividual(ciId + "-" + att.getAttributeDefinition().getName() + ":" + att.getAttributeDefinition().getTypeName(), attClass);
        }
        // datatype properties
        DatatypeProperty valueProp = model.getDatatypeProperty(MODELNAME + "value");
        attInd.setPropertyValue(valueProp, model.createTypedLiteral(att.getValue()));

        // object properties
        ObjectProperty attDefProp = model.getObjectProperty(MODELNAME + "attributeDefinition");
        OntClass attDef = model.getOntClass(MODELNAME + "AttributeDefinition");
        Individual attDefInd = model.getIndividual(att.getAttributeDefinition().getName() + ":" + att.getAttributeDefinition().getTypeName());
        if (attDefInd == null) {
            attDefInd = model.createIndividual(att.getAttributeDefinition().getName() + ":" + att.getAttributeDefinition().getTypeName(), attDef);
            DatatypeProperty nameProp = model.getDatatypeProperty(MODELNAME + "name");
            DatatypeProperty typeProp = model.getDatatypeProperty(MODELNAME + "type");
            attDefInd.addProperty(nameProp, att.getAttributeDefinition().getName());
            attDefInd.addProperty(typeProp, att.getAttributeDefinition().getTypeName());
        }
        attInd.addProperty(attDefProp, attDefInd);

        return attInd;
    }

    public Resource addClass(ContextClass contextClass) throws ContextException {
        Individual clInd = model.getIndividual(contextClass.getName());
        if (clInd == null) {
            OntClass clClass = model.getOntClass(MODELNAME + "ContextClass");
            clInd = model.createIndividual(contextClass.getName(), clClass);

            // datatype properties.
            DatatypeProperty nameProp = model.getDatatypeProperty(MODELNAME + "name");
            clInd.addProperty(nameProp, contextClass.getName());

            // object properties
            if (contextClass.subclassOf() != null) {
                Individual superClassInd = (Individual) addClass(contextClass.subclassOf());
                ObjectProperty superClassProp = model.getObjectProperty(MODELNAME + "superClass");
                clInd.addProperty(superClassProp, superClassInd);
            }

            ObjectProperty attDefsProp = model.getObjectProperty(MODELNAME + "attributeDefinitions");
            for (Enumeration e = contextClass.getAttributeDefinitions(); e.hasMoreElements();) {
                Individual attDefInd = (Individual) addAttributeDefinition((AttributeDefinition) e.nextElement());
                clInd.addProperty(attDefsProp, attDefInd);
            }

            ObjectProperty equProp = model.getObjectProperty(MODELNAME + "equivalentClasses");
            for (Enumeration e = contextClass.getEquivalentClasses(); e.hasMoreElements();) {
                Individual eqClass = (Individual) addClass((ContextClass) e.nextElement());
                clInd.addProperty(equProp, eqClass);
            }

            ObjectProperty intOfProp = model.getObjectProperty(MODELNAME + "intersectionOf");
            for (Enumeration e = contextClass.getIntersections(); e.hasMoreElements();) {
                Individual intOf = (Individual) addPropertyDefinition((ContextPropertyDefinition) e.nextElement());
                clInd.addProperty(intOfProp, intOf);
            }

            ObjectProperty propDefsProp = model.getObjectProperty(MODELNAME + "propertyDefinitions");
            for (Enumeration e = contextClass.getPropertyDefinitions(); e.hasMoreElements();) {
                Individual propDefsInd = (Individual) addPropertyDefinition((ContextPropertyDefinition) e.nextElement());
                clInd.addProperty(propDefsProp, propDefsInd);
            }
        }
        return clInd;
    }

    public Resource addAttributeDefinition(AttributeDefinition ad) throws ContextException {
        OntClass attDef = model.getOntClass(MODELNAME + "attributeDefinition");
        Individual attDefInd = model.getIndividual(ad.getName() + ":" + ad.getTypeName());
        if (attDefInd == null) {
            attDefInd = model.createIndividual(ad.getName() + ":" + ad.getTypeName(), attDef);
            DatatypeProperty nameProp = model.getDatatypeProperty(MODELNAME + "name");
            DatatypeProperty typeProp = model.getDatatypeProperty(MODELNAME + "type");
            attDefInd.addProperty(nameProp, ad.getName());
            attDefInd.addProperty(typeProp, ad.getTypeName());
        }
        return attDefInd;
    }

    public Resource addPropertyDefinition(ContextPropertyDefinition cpd) throws ContextException {
        OntClass propDef = model.getOntClass(MODELNAME + "ContextPropertyDefinition");
        String propDefName = cpd.getName() + "(";
        for (Enumeration e = cpd.getDomains(); e.hasMoreElements();) {
            ContextPropertySpace cs = (ContextPropertySpace) e.nextElement();
            propDefName += cs.getContextClass();
        }
        propDefName += "->";
        for (Enumeration e = cpd.getRanges(); e.hasMoreElements();) {
            ContextPropertySpace cs = (ContextPropertySpace) e.nextElement();
            propDefName += cs.getContextClass();
        }
        propDefName += ")";
        Individual propDefInd = model.getIndividual(propDefName);

        if (propDefInd == null) {
            propDefInd = model.createIndividual(propDefName, propDef);
            DatatypeProperty hasInvProp = model.getDatatypeProperty(MODELNAME + "hasInverse");
            DatatypeProperty funProp = model.getDatatypeProperty(MODELNAME + "functional");
            DatatypeProperty nameProp = model.getDatatypeProperty(MODELNAME + "name");
            DatatypeProperty symProp = model.getDatatypeProperty(MODELNAME + "symmetric");
            DatatypeProperty traProp = model.getDatatypeProperty(MODELNAME + "transitive");
            propDefInd.addProperty(hasInvProp, "" + cpd.hasInverse());
            propDefInd.addProperty(funProp, "" + cpd.isFunctional());
            propDefInd.addProperty(nameProp, "" + cpd.getName());
            propDefInd.addProperty(symProp, "" + cpd.isSymmetric());
            propDefInd.addProperty(traProp, "" + cpd.isTransitive());

            if (cpd.getInverse() != null) {
                ObjectProperty invProp = model.getObjectProperty(MODELNAME + "inverse");
                Individual inverse = (Individual) addPropertyDefinition(cpd.getInverse());
                propDefInd.addProperty(invProp, inverse);
            }

            ObjectProperty domainsProp = model.getObjectProperty(MODELNAME + "domains");
            for (Enumeration e = cpd.getDomains(); e.hasMoreElements();) {
                Individual domInd = (Individual) addContextSpace((ContextPropertySpace) e.nextElement());
                propDefInd.addProperty(domainsProp, domInd);
            }

            ObjectProperty rangesProp = model.getObjectProperty(MODELNAME + "ranges");
            for (Enumeration e = cpd.getRanges(); e.hasMoreElements();) {
                Individual ranInd = (Individual) addContextSpace((ContextPropertySpace) e.nextElement());
                propDefInd.addProperty(rangesProp, ranInd);
            }
        }
        return propDefInd;
    }

    /**
     * add a new ContextPropertySpace to the jena database
     */
    public Resource addContextSpace(ContextPropertySpace cs) throws ContextException {
        OntClass csClass = model.getOntClass(MODELNAME + "ContextSpace");
        String csName = "ContextSpace-" + cs.getContextClass() + "-" + cs.getMinCardinality() + "-" + cs.getMaxCardinality() + "-" + cs.names[cs.getRestrictionType()];
        Individual csInd = model.getIndividual(csName);
        if (csInd == null) {
            csInd = model.createIndividual(csName, csClass);
            DatatypeProperty classProp = model.getDatatypeProperty(MODELNAME + "className");
            DatatypeProperty maxProp = model.getDatatypeProperty(MODELNAME + "maxCardinality");
            DatatypeProperty minProp = model.getDatatypeProperty(MODELNAME + "minCardinality");
            DatatypeProperty rtProp = model.getDatatypeProperty(MODELNAME + "restrictionType");
            csInd.addProperty(maxProp, "" + cs.getMaxCardinality());
            csInd.addProperty(minProp, "" + cs.getMinCardinality());
            csInd.addProperty(rtProp, ContextPropertySpace.names[cs.getRestrictionType()]);
            csInd.addProperty(classProp, cs.getContextClass());
        }
        return csInd;
    }

    /**
     * add a new property to the jena database
     */
    public Resource addProperty(ContextProperty p, String ciName) throws ContextException {
        OntClass cpClass = model.getOntClass(MODELNAME + "ContextProperty");
        Individual cpInd = model.createIndividual(ciName + "-" + p.getPropertyDefinition().getName(), cpClass);

        // properties
        ObjectProperty diProp = model.getObjectProperty(MODELNAME + "domainInstances");
        ObjectProperty riProp = model.getObjectProperty(MODELNAME + "rangeInstances");
        ObjectProperty pdProp = model.getObjectProperty(MODELNAME + "propertyDefinition");
        Individual pdInd = (Individual) addPropertyDefinition(p.getPropertyDefinition());
        cpInd.addProperty(pdProp, pdInd);
        for (Enumeration e = p.getDomains(); e.hasMoreElements();) {
            Individual dInd;
            dInd = (Individual) addContextualInformation((ContextualInformation) e.nextElement());
            cpInd.addProperty(diProp, dInd);
        }
        for (Enumeration e = p.getRanges(); e.hasMoreElements();) {
            Individual rInd = (Individual) addContextualInformation((ContextualInformation) e.nextElement());
            cpInd.addProperty(riProp, rInd);
        }
        return cpInd;
    }

    public void addContextToHistorical(Context c) throws ContextException {

        String name = "historical" + c.getName();
        OntResource hContext = model.getOntResource(name);

        if (hContext == null) {
            OntClass historicalContext = model.getOntClass(MODELNAME + "HistoricalContext");
            hContext = model.createIndividual(name, historicalContext);
            // datatype properties
            DatatypeProperty idProp = model.getDatatypeProperty(MODELNAME + "id");
            hContext.addProperty(idProp, name);
            DatatypeProperty nameProp = model.getDatatypeProperty(MODELNAME + "name");
            hContext.addProperty(nameProp, name);
        }

        OntClass simpleContext = model.getOntClass(MODELNAME + "SimpleContext");
        Calendar calendar = Calendar.getInstance();
        String instant = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(calendar.getTime());
        String newContext = c.getName() + "|" + instant;

        Individual simpleInd = model.createIndividual(newContext, simpleContext);


        // datatype properties
        DatatypeProperty idProp = model.getDatatypeProperty(MODELNAME + "id");
        simpleInd.addProperty(idProp, newContext);
        DatatypeProperty nameProp = model.getDatatypeProperty(MODELNAME + "name");
        simpleInd.addProperty(nameProp, newContext);

        try {
            ObjectProperty data = model.getObjectProperty(MODELNAME + "data");
            for (Enumeration e = c.getContextualInformation().getContextualInformation(); e.hasMoreElements();) {

                ContextualInformation ci = (ContextualInformation) e.nextElement();
                ContextClass cc = ci.getContextClass();
                String nameCc = ci.getName() + "@" + c.getName() + "|" + instant;
                String description = ci.getDescription();
                Hashtable attributes = new Hashtable();
                Vector<ContextProperty> properties = new Vector();
                for (Enumeration i = ci.getAttributes(); i.hasMoreElements();) {
                    Attribute att = (Attribute) i.nextElement();
                    attributes.put(att.getAttributeDefinition(), att);
                }
                for (Enumeration p = ci.getProperties(); p.hasMoreElements();) {
                    ContextProperty prop = (ContextProperty) p.nextElement();
                    properties.addElement(prop);
                }

                ContextualInformation newCi = new ContextualInformation(cc, nameCc, description, attributes, properties);
                Individual ciInd = (Individual) addContextualInformation(newCi);
                simpleInd.addProperty(data, ciInd);
            }
        } catch (ContextualInformationAbsentException ex) {
            ex.printStackTrace();
            //throw new ContextException("Unable to get contextual information from simple context "+sc.getId());
            System.err.println("Unable to get contextual information from simple context " + c.getId());
        }

        ObjectProperty contexts = model.getObjectProperty(MODELNAME + "hContexts");
        hContext.addProperty(contexts, simpleInd);
    }
}